package Client;

import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.Iterator;

import Carte.Calendrier;
import Carte.Trajet;
import Carte.Ville;
import Passager.Passager;
import Serveur.NoPlaceReservationException;
import Serveur.Serveur;
import Services.ServiceChoisis;

/**
 * Une reservation permet de sauvegarder un nombre de personne pour un certain
 * trajet � une certain date.
 * 
 * @author belli
 * 
 */
public class Reservation {
	/**
	 * Identifiant unique permettant d'identifier une reservation
	 */
	private int IDReservation;

	/**
	 * Lien vers le serveur pour les bases de données
	 */
	Serveur serveur;

	private int IDTrajet;

	private GregorianCalendar date;

	private int nbPersonne;

	private ArrayList<Integer> ListedesPassager;

	//Pour chaque passager est associé un service
	private ArrayList<ServiceChoisis> ListeServiceChoisi;

	/*
	 * Permet de savoir si une reservation à été reservée
	 */
	static boolean reserver;

	/**
	 * Constructeur normal
	 * La reservation n'est pas ajouté dans le serveur, elle est 
	 * ajoutée lors de l'execution de la méthode reserver
	 * 
	 * @param serv
	 * @param trajet
	 * @param ListePassager
	 * @param ListeServiceChoisi
	 */
	public Reservation(Serveur serv, int idTrajet, ArrayList<Integer> ListePassager,  ArrayList<ServiceChoisis> ListeServiceChoisi) {
		this.IDReservation = serv.getIdentifiantValideReservation();
		this.date = new GregorianCalendar();
		this.ListedesPassager = ListePassager;

		//Verifier que les arguements ont la bonne taille
		if(ListePassager.size() != ListeServiceChoisi.size()){
			throw new IllegalArgumentException();
		}
		this.IDTrajet = idTrajet;
		this.nbPersonne = ListePassager.size();
		this.ListeServiceChoisi = ListeServiceChoisi;
		this.serveur = serv;
		reserver = false;
	}


	/**
	 * Constructeur pour la serialisation
	 * @param serv
	 * @param idTrajet
	 * @param ListePassager
	 * @param ListeServiceChoisi
	 * @param IDReservation
	 * @param dateGC
	 */
	public Reservation(Serveur serv, int idTrajet, ArrayList<Integer> ListePassager,
			ArrayList<ServiceChoisis> ListeServiceChoisi, int IDReservation, GregorianCalendar dateGC) {
		this.IDReservation = IDReservation;
		this.date = dateGC;
		this.ListedesPassager = ListePassager;

		//Verifier que les arguements ont la bonne taille
		if(ListePassager.size() != ListeServiceChoisi.size()){
			throw new IllegalArgumentException();
		}
		this.IDTrajet = idTrajet;
		this.nbPersonne = ListePassager.size();
		this.ListeServiceChoisi = ListeServiceChoisi;
		this.serveur = serv;
	}



	/**
	 * Savoir si on peut reserver entre deux villes a certaine dates pour un
	 * certain nombre de personne
	 * 
	 * @param ListeTrajets
	 * @param villeDepart
	 * @param villeArrive
	 * @param datedepart
	 * @param dateArrive
	 * @param nbPersonne
	 * @return Liste de trajet correspondant
	 * @throws NoPlaceReservationException
	 */
	public static ArrayList<Trajet> peutReserver(
			ArrayList<Trajet> ListeTrajets, Ville villeDepart,
			Ville villeArrive, GregorianCalendar datedepart,
			GregorianCalendar dateArrive, int IDTransport, int nbPersonne)
			throws NoPlaceReservationException {

		// Annuler le nombre de personne si la valeur est negative
		if (nbPersonne < 0) {
			nbPersonne = 0;
			return null;
		}

		// Filtrer la liste des trajets
		ArrayList<Trajet> ListeTrajetfiltrer = Trajet.searchTrajet(
				ListeTrajets, villeDepart, villeArrive, datedepart, dateArrive,
				IDTransport, nbPersonne);
		ArrayList<Trajet> ListeNewTrajet = new ArrayList<Trajet>();
		int maxPlacedispo = 0;

		if (!ListeTrajetfiltrer.isEmpty()) {
			Iterator<Trajet> it = ListeTrajetfiltrer.iterator();
			while (it.hasNext()) {
				Trajet current = it.next();
				if (current.peutReserver(nbPersonne)) {
					ListeNewTrajet.add(current);
				} else {
					if (current.getNbPlaceDispo() > maxPlacedispo) {
						maxPlacedispo = current.getNbPlaceDispo();
					}
				}
			}

			// Si le nombre de trajet trouve
			if (ListeNewTrajet.isEmpty()) {
				throw new NoPlaceReservationException(maxPlacedispo);
			}
			return ListeNewTrajet;
		} else {
			throw new NoPlaceReservationException(maxPlacedispo);
		}
	}

	/**
	 * Savoir si on peut reserver pour un certain trajet
	 * 
	 * @param ListeTrajets
	 * @param t
	 * @param nbpers
	 * @return true si la reservation est possible false sinon
	 * @throws NoPlaceReservationException
	 */
	public static boolean peutReserver(ArrayList<Trajet> ListeTrajets,
			Trajet t, int nbpers) {
		// Opposer le nombre de personne si la valeur est negative
		if (nbpers < 0) {
			nbpers = -nbpers;
		}

		ArrayList<Trajet> ListeTrajFiltre;
		try {
			ListeTrajFiltre = peutReserver(ListeTrajets, t.getVilleDepart(), t
					.getVilleArrive(), t.getDatedepart(), t.getDatearrive(), t
					.getTransport(), nbpers);
		} catch (NoPlaceReservationException e) {
			// TODO Auto-generated catch block
			return false;
		}
		return !ListeTrajFiltre.isEmpty();
	}

	/**
	 * Savoir si on peut reserver avec un reservation
	 * 
	 * @return true si la reservation est possible false sinon
	 * @throws NoPlaceReservationException
	 */
	public boolean peutReserver(Serveur serv) {
		//Recuperer le trajet denommé par sont identifiant
		Trajet traj = serv.getTrajetListe(this.IDTrajet);
		return peutReserver(serv.getListeTrajet(), traj, this.nbPersonne);
	}

	/**
	 * Effectuer un reservation pour un trajet et un nombre de personne
	 * La reservation crée est automatiquement enregistrée dans le serveur
	 * 
	 * @param ListeTrajets
	 * @param t
	 * @param nbpers
	 * @return vrai si la reservation est possible faux sinon
	 * @throws NoPlaceReservationException
	 */
	public static boolean Reserver(Serveur serv, int IDTrajet, ArrayList<Integer> ListePassager, ArrayList<ServiceChoisis> ListeServiceChoisi) {

		/**
		 * Verifier que la reservation n'ai pas ete reservee
		 */
		if(reserver){
			return false;
		}

		//Reserver la reservation
		//
		reserver = true;

		//Recuperer le nombre de personne
		//
		int nbpersonne  = ListePassager.size();

		//Verifier la validitee des parametres 
		//
		if(ListePassager.size() != ListeServiceChoisi.size()){
			return false;
		}

		//Recuperer le trajet dans le serveur
		//
		Trajet t = serv.getTrajetListe(IDTrajet);

		//Recuperer la liste des trajets correspondant a�la recherche
		//
		ArrayList<Trajet> ListeFiltreTrajet;
		try {
			ListeFiltreTrajet = peutReserver(serv.getListeTrajet(), t
					.getVilleDepart(), t.getVilleArrive(), t.getDatedepart(), t
					.getDatearrive(), t.getIdTransport(), nbpersonne);
		} catch (NoPlaceReservationException e) {
			return false;
		}

		//Parcourir la liste des resultats
		//
		Iterator<Trajet> it = ListeFiltreTrajet.iterator();


		if (!ListeFiltreTrajet.isEmpty()) {
			// choisir le premier trajet disponible
			//
			Trajet traj = it.next();

			//Recuperer l'emplacement du trajet dans le serveur
			//
			int index = serv.getListeTrajet().indexOf(traj);

			//Effectuer une reservation pour ce trajet
			//
			traj.reserver(nbpersonne);

			// Modifier le trajet dans la liste du serveur
			//
			serv.modifierTrajet(traj, index);

			//Création de la nouvelle reservation
			//
			Reservation res = new Reservation(serv, traj.getIDTrajet(), ListePassager, ListeServiceChoisi);

			// Ajouter la reservation dans la liste des reservations
			//
			serv.addReservation(res);

			return true;
		} else {
			return false;
		}
	}

	public ArrayList<Integer> getListePassager() {
		return ListedesPassager;
	}

	public ArrayList<ServiceChoisis> getListeServiceChoisi() {
		return ListeServiceChoisi;
	}

	/**
	 * Reserver pour une reservation donn�e Ajout de la reservation dans la
	 * liste du serveur
	 * 
	 * @return
	 * @throws NoPlaceReservationException
	 */
	public boolean reserver() {
		return Reserver(serveur, IDTrajet, ListedesPassager, ListeServiceChoisi);
	}


	public void print() {
		System.out.println(toString());
	}

    @Override
    public String toString() {
        String retour = "";
        retour += "Effectuee le " + Calendrier.toString(date);
        retour += "\nNb place(s) reservee(s) : " + nbPersonne;
        retour += "\nPrix :" + this.getPrixTotalReservation();
        retour += serveur.getTrajetListe(IDTrajet).toString();

		return retour;
	}
        
	/**
	 * Redefinition de la methode equals
	 */
	@Override
	public boolean equals(Object obj) {
		Reservation Res = (Reservation) obj;

		if(this.getIdReservation() == Res.getIdReservation()){
			return true;
		}

		if (!this.date.equals(Res.date)) {
			return false;
		}
		if (!(this.nbPersonne == Res.nbPersonne)) {
			return false;
		}
		if (!(this.IDTrajet == Res.IDTrajet)) {
			return false;
		}
		if(!(this.IDReservation == Res.getIdReservation())){
			return false;
		}
		if(!(this.getListePassager().size() == Res.getListePassager().size())){
			return false;
		}
		if(!this.getListePassager().equals(Res.getListePassager())){
			return false;
		}
		if(!this.getListeServiceChoisi().equals(Res.getListeServiceChoisi())){
			return false;
		}
		return true;
	}

	/**
	 * Annuler une reservation
	 * 
	 * @param ListeReservation
	 * @param R
	 * @return vrai si l'annulation � pu se faire
	 */
	public boolean annuler(ArrayList<Reservation> ListeReservation,
			Reservation R) {
		if (!ListeReservation.contains(R)) {
			System.out
			.println("La reservation n'est pas presente dans la liste");
			return false;
		}

		// Supprimer la reservation de la liste des reservations dans le serveur
		ListeReservation.remove(R);

		// Remettre le nombre de place disponible.
		Trajet traj = R.getTrajet();
		int placeReserve = R.getNbPersonne();
		if (!traj.annulerReservation(placeReserve)) {
			System.out.println("Impossible de supprimer la reservation");
		}

		return true;
	}

	/**
	 * Rechercher une reservation par trajet et date
	 * 
	 * @param ListeReservation
	 * @param trajet
	 * @param date
	 * @return Liste de reservation correpondante
	 */
	public static ArrayList<Reservation> searchReservation(
			ArrayList<Reservation> ListeReservation, Trajet trajet,
			GregorianCalendar date) {
		ArrayList<Reservation> ListeReservationRetour = new ArrayList<Reservation>();
		Iterator<Reservation> it = ListeReservation.iterator();

		while (it.hasNext()) {
			Reservation current = it.next();
			if (current.getDate().equals(date)
					&& current.getTrajet().equals(trajet)) {
				ListeReservationRetour.add(current);
				ListeReservationRetour.add(current);
				ListeReservationRetour.add(current);
				ListeReservationRetour.add(current);
			}
		}

		return ListeReservation;
	}

	/**
	 * Obtenir le prix de la reservation en faisant la somme de
	 * toutes les personnes
	 * @return prix total
	 */
	public double getPrixTotalReservation(){
		double total = 0;

		//Boucler sur les services afin de calculer le prix 
		//de ces derniers
		for (ServiceChoisis sc : ListeServiceChoisi) {
			total += serveur.getTrajetListe(IDTrajet).getPrixBillet() + sc.getPrixServices();
		}

		return total;
	}

	/**
	 * Savoir si un passager est dans la liste
	 * @param pass
	 * @return
	 */
	public boolean containtPassenger(int IDPassager){
		return ListedesPassager.contains(IDPassager);
	}

	/**
	 * Obtenir le service d'un passager
	 * @param pass
	 * @return
	 */
	public ServiceChoisis getServiceFromPassager(Passager pass){
		//verifier la validité des parametres 
		if(!containtPassenger(pass.getIdPassager())){
			System.out.println("Le passager n'existe pas");
			return null;
		}

		int indice = ListedesPassager.indexOf(pass.getIdPassager());
		return ListeServiceChoisi.get(indice);
	}

	/**
	 * Supprimer un passager
	 * Attention il faut supprimer un passager avant de reserver()
	 * @param pass
	 */
	public void supprimerPassager(Passager pass){
		//verifier la validité des parametres 
		if(!containtPassenger(pass.getIdPassager())){
			System.out.println("Le passager n'existe pas");
			return;
		}

		int indice = ListedesPassager.indexOf(pass.getIdPassager());
		ListedesPassager.remove(indice);
		ListeServiceChoisi.remove(indice);

		/**
		 * Si la reservation à été reservée il faut supprimer le passager
		 * dans le trajet concernée
		 */
		if(reserver){
			int IDTrajet = getIdTrajet();
			Trajet traj = serveur.getTrajetListe(IDTrajet);
			traj.annulerReservation(1);
		}
	}

	/**
	 * Ajout d'un passager à la liste contenu dans la reservation
	 * @param pass
	 * @param service
	 */
	public boolean addPassager(Passager pass, ServiceChoisis service){
		if(serveur.getIndexOfPassager(pass) == -1){
			serveur.addPassager(pass);
		}
		
		if(ListedesPassager.contains(pass.getIdPassager()))	return false;
		
		/**
		 * Verifier que la commande n'a pas ete passee
		 */
		if(reserver){
			System.out.println("La commande est deja reservee");
			return false;
		}
		
		nbPersonne++;
		ListedesPassager.add(pass.getIdPassager());
		ListeServiceChoisi.add(service);
		
		return true;
	}

	/**
	 * Modifier le service d'un passager
	 * @param pass
	 * @param serv
	 */
	public void setService(Passager pass, ServiceChoisis serv){
		//verifier la validité des parametres 
		if(!containtPassenger(pass.getIdPassager())){
			System.out.println("Le passager n'existe pas");
			return;
		}

		int indice = ListedesPassager.indexOf(pass.getIdPassager());
		ListeServiceChoisi.set(indice, serv);
	}

	/**
	 * Acceseur de consultation
	 * 
	 * @return Trajet
	 */
	public Trajet getTrajet() {
		return serveur.getTrajetListe(IDTrajet);
	}

	/**
	 * Accesseur pour obtenir la date
	 * 
	 * @return Date
	 */
	public GregorianCalendar getDate() {
		return date;
	}

	/**
	 * Accesseur pour obtenir le nombre de personne
	 * 
	 * @return nombre de personne int
	 */
	public int getNbPersonne() {
		return nbPersonne;
	}

	public int getIdReservation(){
		return IDReservation;
	}

	public Serveur getServeur(){
		return serveur;
	}
	public int getIdTrajet()
	{
		return IDTrajet;
	}

	public static void setReserver(boolean reserver) {
		Reservation.reserver = reserver;
	}
}
